package com.sprite.framework.component;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import com.sprite.utils.UtilString;
import com.sprite.utils.UtilURL;
import com.sprite.utils.UtilXml;


/**
 * sprite.xml 操作
 * @author Jack
 */
public class SpriteConfig {
	public static final String SPRITE_COMPONENT_XML_FILENAME = "sprite.xml";

	public static List<SpriteConfig> getAllComponents() throws SpriteException{
		List<URL> urls;
		try {
			urls = UtilURL.fromResources(SPRITE_COMPONENT_XML_FILENAME);
		} catch (IOException e) {
			throw new SpriteException("Error scanning the component config file ", e);
		}

	
		List<SpriteConfig>  configs = new LinkedList<>();
		for(URL url : urls){
			SpriteConfig config = new SpriteConfig(url);
			configs.add(config);
		}
		
		return configs;
	}

	private Path configPath;
	private Document document;
	
	public SpriteConfig(URL url) throws SpriteException{
		init(url);
	}

	public URI relativeResource(String relativePath) {
		char ch = relativePath.charAt(0);
		if(ch == '/' || ch=='.'){
			throw new IllegalArgumentException("path must be relative");
		}

		return configPath.getParent().resolve(relativePath).toUri();
	}
	
	private void init(URL xmlUrl) throws SpriteException{
		try {
			URI uri = xmlUrl.toURI();
			if("jar".equals(uri.getScheme())){
				Map<String, String> env = new HashMap<>();
				env.put("create", "true");
				FileSystems.newFileSystem(uri, env);
			}
			configPath = Paths.get(uri);
		} catch (Exception e1) {
			throw new SpriteException(e1);
		}

		try {
			document = UtilXml.readXmlDocument(xmlUrl, false);
		} catch (SAXException e) {
			throw new SpriteException("Error reading the component config file: " + xmlUrl, e);
		} catch (ParserConfigurationException e) {
			throw new SpriteException("Error reading the component config file: " + xmlUrl, e);
		} catch (IOException e) {
			throw new SpriteException("Error reading the component config file: " + xmlUrl, e);
		}
	}

	/**
	 * 配置解析SpriteConfig，只有配置项中的loaders属性与指定的loaders相匹配才被装载
	 * @param loaders 指定的有效装载器
	 * @throws SpriteException 配置异常
	 */
	public void configure(List<String> loaders) throws SpriteException {
		for(Element element : UtilXml.childElementList(document.getDocumentElement())) {
			String nodeName = element.getNodeName();
			String loader = getAttribute(element, element.getAttribute("loaders"), "main");
			if(!hasSameOne(loaders, UtilString.commaDelimiteToStringList(loader))) {
				continue;
			}
			
			ConfigHandler.getHandler(nodeName).handle(element, this);
		}
	}
	
	private String getAttribute(Element element, String name, String defaultValue) {
		String value = element.getAttribute(name);
		if(UtilString.isEmpty(value)) {
			return defaultValue;
		}
		
		return value;
	}
	private <T> boolean hasSameOne(Collection<T> list, Collection<T> list1) {
		if(list == null || list1 == null) {
			return false;
		}
		
		for(T t: list) {
			if(t== null) continue;
			for(T t2: list1) {
				if(t2 != null && t.equals(t2)) {
					return true;
				}
			}
		}
		
		return false;
	}

}
